﻿#region Header
// Copyright 2007-2011, Jerónimo Milea
// 
// This file is part of NetJsWire.
// 
//     NetJsWire is free software: you can redistribute it and/or modify
//     it under the terms of the GNU General Public License as published by
//     the Free Software Foundation, either version 3 of the License, or
//     (at your option) any later version.
// 
//     NetJsWire is distributed in the hope that it will be useful,
//     but WITHOUT ANY WARRANTY; without even the implied warranty of
//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//     GNU General Public License for more details.
// 
//     You should have received a copy of the GNU General Public License
//     along with NetJsWire.  If not, see <http://www.gnu.org/licenses/>.
#endregion

using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Drawing.Design;
using System.Linq;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Windows.Forms.Layout;
using NetJsWire.Lite.Converters;
using NetJsWire.Lite.Designers;
using NetJsWire.Lite.Editors;
using NetJsWire.Lite.Widgets.UI.Form;
using NetJsWire.Lite.Widgets.UI.Grid;
using NetJsWire.Lite.Widgets.UI.Layout;
using NetJsWire.Lite.Widgets.UI.Layout.Config;
using Newtonsoft.Json;

namespace NetJsWire.Lite.Widgets.UI
{
    //[Designer( typeof( DocumentDesigner ), typeof( IRootDesigner ) )]
    [Designer( typeof( ContainerDesigner ) )]
    [ToolboxItem( false )]
    [JsonObject( MemberSerialization = MemberSerialization.OptIn )]
    public class Container : BoxComponent, IContainerControl
    {
        private Control _activeControl;
        private int _activeItem;
        private float _columnWidth;
        private LabelAlign _labelAlign = LabelAlign.Left;
        private int _labelWidth = 100;
        private LayoutType _layout = LayoutType.Auto;
        private LayoutConfig _layoutConfig;

        //private ComponentCollection _components;
        //private System.Collections.Hashtable _componentsTable = new System.Collections.Hashtable();

        public Container()
            : this( null )
        {
        }

        public Container( IContainer container )
            : base( container )
        {
            base.Xtype = "container";
            ControlAdded += ContainerControlAdded;
            ControlRemoved += ContainerControlRemoved;
            LabelWidth = 100;
        }

        [DefaultValue( 0 )]
        public virtual int ActiveItem
        {
            get { return _activeItem; }
            set
            {
                _activeItem = value;
                PerformLayout();
                InvalidateWindow();
            }
        }

        //[JsonProperty( "items" )]
        [Browsable( false )]
        [EditorBrowsable( EditorBrowsableState.Never )]
        [DefaultValue( null )]
        internal IList Items
        {
            get
            {
                var controls =
                    Controls.OfType<BaseComponent>().Where(
                        c =>
                        ( !( c is ButtonLW ) || !( c as ButtonLW ).InButtons ) &&
                        ( !( c is ToolbarControl ) || c.Dock == DockStyle.None ) );
                return controls.Count() > 0 ? controls.ToList() : null;
            }
        }

        [JsonProperty( "items", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore )]
        [Browsable( false )]
        [EditorBrowsable( EditorBrowsableState.Never )]
        [DefaultValue( null )]
        public virtual IEnumerable RenderItems
        {
            get
            {
                var enumerable = Items != null ? Items.OfType<object>() : null;
                var items = enumerable != null ? enumerable.ToList() : null;
                if ( items != null && !string.IsNullOrWhiteSpace( NonDesignableItems ) )
                {
                    items.Add( JsonConvert.DeserializeObject( NonDesignableItems ) );
                }

                return items;
            }
        }

        [DefaultValue( ( string ) null )]
        [Editor( typeof( ScriptEditor ), typeof( UITypeEditor ) )]
        [Description( "Items you want to add that are not present for design. It must be a valid JSON object representation." )]
        public virtual string NonDesignableItems
        {
            get;
            set;
        }

        [DefaultValue( 0f )]
        [JsonProperty( "columnWidth" )]
        [Description( "The percentage width to use if parent.layout == column." )]
        public float ColumnWidth
        {
            get { return _columnWidth; }
            set
            {
                _columnWidth = value;
                if ( Parent != null )
                {
                    var parent = Parent as Container;
                    if ( parent != null )
                    {
                        if ( parent.Layout == LayoutType.Column )
                        {
                            parent.PerformLayout();
                            parent.InvalidateWindow();
                        }
                    }
                }
            }
        }

        [DefaultValue( LayoutType.Auto )]
        [JsonProperty( "layout" )]
        [JsonConverter( typeof( LowerCaseEnumConverter ) )]
        [RefreshProperties( RefreshProperties.All )]
        [Description( "The layout type to be used by this container" )]
        public new virtual LayoutType Layout
        {
            get { return _layout; }
            set
            {
                if ( !( this is TabPanelLW ) && value == LayoutType.Card )
                    throw new InvalidOperationException( "Cannot set layout type 'card' on a component that is not a TabPanel" );

                _layout = value;
                LayoutConfig = null;
                PerformLayout();
            }
        }

        [Browsable( true )]
        [JsonProperty( "layoutConfig" )]
        [RefreshProperties( RefreshProperties.All )]
        [DesignerSerializationVisibility( DesignerSerializationVisibility.Content )]
        public LayoutConfig LayoutConfig
        {
            get
            {
                if ( _layoutConfig == null )
                {
                    _layoutConfig = GetLayoutConfig();
                }
                return _layoutConfig;
            }
            set { _layoutConfig = value; }
        }

        [Browsable( false )]
        [EditorBrowsable( EditorBrowsableState.Never )]
        internal virtual int BorderWidth
        {
            get
            {
                return 1;
            }
        }

        public override LayoutEngine LayoutEngine
        {
            get
            {
                switch ( _layout )
                {
                    case LayoutType.Auto:
                        return new AutoLayoutEngine();
                    case LayoutType.Column:
                        return new ColumnLayoutEngine();
                    case LayoutType.Form:
                        return new FormLayoutEngine();
                    case LayoutType.Border:
                        return new BorderLayoutEngine();
                    case LayoutType.Fit:
                        return new FitLayoutEngine();
                    case LayoutType.Card:
                        return new CardLayoutEngine();
                    case LayoutType.Toolbar:
                        return new ToolbarLayoutEngine();
                    case LayoutType.Hbox:
                        return new HBoxLayoutEngine();
                    case LayoutType.Vbox:
                        return new VBoxLayoutEngine();
                    case LayoutType.Anchor:
                    case LayoutType.Absolute:
                        return new AnchorLayoutEngine();
                    default:
                        return base.LayoutEngine;
                }
            }
        }

        //[DefaultValue( LabelAlign.Left )]
        [JsonProperty( "labelAlign" )]
        [JsonConverter( typeof( LowerCaseEnumConverter ) )]
        [Description( "The label alignment value used for the text-align specification for the container. This property cascades to child containers and can be overridden on any child container (e.g., a fieldset can specify a different labelAlign for its fields)." )]
        public LabelAlign LabelAlign
        {
            get { return _labelAlign; }
            set
            {
                _labelAlign = value;
                PerformLayout();
                Invalidate( true );
            }
        }

        [JsonProperty( "labelWidth", DefaultValueHandling = DefaultValueHandling.Ignore )]
        [Description( "The width of labels in pixels. This property cascades to child containers and can be overridden on any child container (e.g., a fieldset can specify a different labelWidth for its fields) (defaults to 100).\nNote that this config option has no effect when the container layout is not Form" )]
        public int LabelWidth
        {
            get
            {
                return _labelWidth;
            }
            set
            {
                _labelWidth = value;
                PerformLayout();
                Invalidate( true );
            }
        }

        void ContainerControlRemoved( object sender, ControlEventArgs e )
        {
            PerformLayout();
            InvalidateWindow();
        }

        void ContainerControlAdded( object sender, ControlEventArgs e )
        {
            if ( e.Control is BaseComponent )
                ( ( BaseComponent ) e.Control ).ContainerControl = this;
            PerformLayout();
            InvalidateWindow();
        }

        public bool ShouldSerializeLayoutConfig()
        {
            return LayoutConfig.ShouldSerialize();
        }

        protected void ResetLayoutConfig()
        {
            LayoutConfig = GetLayoutConfig();
        }

        private LayoutConfig GetLayoutConfig()
        {
            switch ( Layout )
            {
                case LayoutType.Absolute:
                case LayoutType.Anchor:
                    return new AnchorLayoutConfig();
                case LayoutType.Form:
                    return new FormLayoutConfig();
                case LayoutType.Hbox:
                    return new HBoxLayoutConfig();
                case LayoutType.Vbox:
                    return new VBoxLayoutConfig();
                case LayoutType.Fit:
                case LayoutType.Border:
                case LayoutType.Accordion:
                case LayoutType.Card:
                case LayoutType.Column:
                case LayoutType.Table:
                case LayoutType.Toolbar:
                case LayoutType.Auto:
                default:
                    return new LayoutConfig();
            }
        }

        internal virtual BaseComponent GetComponentAtPoint( Point pt )
        {
            return ( from Control control in Controls where control.Bounds.Contains( pt ) select control as BaseComponent ).FirstOrDefault();
        }


        protected override void OnPaint( PaintEventArgs e )
        {
            base.OnPaint( e );
            if ( Layout != LayoutType.Form )
                return;
            var controls = Items;
            if ( controls == null )
                return;
            var labelAlign = LabelAlign;
            var labelWidth = LabelWidth;
            var labelPadding = 5f;
            var formPanel = FindFormPanelInParents( this );
            if ( formPanel != null )
            {
                //labelAlign = formPanel.LabelAlign;
                labelWidth = formPanel.LabelWidth;
            }

            foreach ( var control in controls.Cast<BaseComponent>().Where( control => !string.IsNullOrWhiteSpace( control.FieldLabel ) || ( control is CompositeField && ( ( CompositeField ) control ).Items.Cast<BaseComponent>().Any( c => !string.IsNullOrWhiteSpace( c.FieldLabel ) ) ) ) )
            {
                var parentLeftPadding = 0;
                var parentTopPadding = 0;
                if ( this is Panel )
                {
                    parentLeftPadding = ( ( Panel ) this ).Padding.Left;
                    parentTopPadding = ( ( Panel ) this ).Padding.Top;
                }

                using ( var font = new Font( "Tahoma", 12, FontStyle.Regular, GraphicsUnit.Pixel ) )
                {
                    using ( var br = new SolidBrush( Color.Black ) )
                    {
                        //var textSize = e.Graphics.MeasureString( ":", font );
                        var sf = new StringFormat
                                     {
                                         Alignment =
                                             labelAlign == LabelAlign.Right ? StringAlignment.Far : StringAlignment.Near,
                                         LineAlignment =
                                             ( labelAlign == LabelAlign.Left || labelAlign == LabelAlign.Right )
                                                 ? StringAlignment.Near
                                                 : StringAlignment.Near,
                                         Trimming = StringTrimming.Character
                                     };


                        var separator = LabelSeparator ?? ":";
                        if ( !string.IsNullOrEmpty( control.LabelSeparator ) )
                        {
                            separator = control.LabelSeparator;
                        }

                        var labelTop = ( control.Top - parentTopPadding ) + 3 + parentTopPadding;
                        if ( labelAlign == LabelAlign.Top )
                        {
                            labelTop -= 20;
                        }

                        var label = control.FieldLabel;
                        if ( control is CompositeField )
                        {
                            var composite = ( CompositeField ) control;
                            label = composite.FieldLabel;
                            if ( string.IsNullOrEmpty( label ) )
                            {
                                var first = true;
                                foreach ( var child in composite.Items.Cast<BaseComponent>() )
                                {
                                    if ( !first )
                                    {
                                        label += ( composite.LabelConnector ?? ", " );
                                    }
                                    label += child.FieldLabel;
                                    first = false;
                                }
                            }
                        }

                        e.Graphics.DrawString( label + separator, font, br,
                                              new RectangleF( -2 + parentLeftPadding,
                                                             labelTop,
                                                             labelWidth + labelPadding,
                                                             control.Height - 3 ), sf );
                    }
                }
            }
        }

        public bool ShouldSerializeItems()
        {
            return Controls.Count > 0;
        }

        internal static FormPanel FindFormPanelInParents( Container container )
        {
            if ( container is FormPanel )
            {
                return ( FormPanel ) container;
            }

            if ( container.Parent != null && container.Parent is FormPanel )
            {
                return ( FormPanel ) container.Parent;
            }

            if ( container.Parent == null || !( container.Parent is Container ) )
                return null;

            return FindFormPanelInParents( container.Parent as Container );
        }

        public bool ShouldSerializeLabelWidth()
        {
            if ( this is GridPanel )
                return false;
            return ( Parent != null && Parent is Container && ( ( Container ) Parent ).Layout == LayoutType.Form && ( ( Container ) Parent ).LabelWidth != LabelWidth ) || LabelWidth != 100;
        }

        protected void ResetLabelWidth()
        {
            LabelWidth = 100;
        }

        public bool ShouldSerializeLabelAlign()
        {
            return LabelAlign != LabelAlign.Left || ( Parent != null && Parent is Container && ( Parent as Container ).LabelAlign != LabelAlign );
        }

        protected void ResetLabelAlign()
        {
            LabelAlign = ( Parent != null && ( ( Container ) Parent ).LabelAlign != LabelAlign ) ? ( Parent as Container ).LabelAlign : LabelAlign.Left;
        }

        #region Implementation of IContainerControl

        /// <summary>
        ///   Activa un control especificado.
        /// </summary>
        /// <returns>
        ///   true si el control se activa correctamente; en caso contrario, false.
        /// </returns>
        /// <param name = "active"><see cref = "T:System.Windows.Forms.Control" /> que se está activando. </param>
        /// <filterpriority>1</filterpriority>
        public bool ActivateControl( Control active )
        {
            if ( Controls.Contains( active ) )
            {
                // Select the control and scroll the control into view if needed.
                active.Select();
                _activeControl = active;
                return true;
            }
            return false;
        }

        /// <summary>
        ///   Obtiene o establece el control que está activo en el control contenedor.
        /// </summary>
        /// <returns>
        ///   <see cref = "T:System.Windows.Forms.Control" /> que está activo actualmente en el control contenedor.
        /// </returns>
        /// <filterpriority>1</filterpriority>
        [Browsable( false ), EditorBrowsable( EditorBrowsableState.Advanced )]
        [DefaultValue( null )]
        public Control ActiveControl
        {
            get { return _activeControl; }
            set
            {
                if ( Controls.Contains( value ) )
                {
                    _activeControl = value;
                }
            }
        }

        #endregion
    }
}
